package com.frank.redisLua;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.UUID;

/**
 * @author 小石潭记
 * @date 2020/11/13 10:03
 * @Description: ${todo}
 */
@RestController
@RequestMapping("/lua")
@Slf4j
public class LuaController {

    @Autowired
    private RedisTemplate redisTemplate;

    /** 释放锁lua脚本 */
    private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";


    @GetMapping("/index")
    public Object index() {
        String lockKey = "123";
        String uuid = UUID.randomUUID().toString();
        boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 3, TimeUnit.MINUTES);
        if (!success) {
            log.info("锁已存在");
        }
        // 执行 lua 脚本
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        // 先获取指定key的值，然后和传入的arg比较是否相等，相等值删除key，否则直接返回0。
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("file/demo.lua")));
        // 指定返回类型
        redisScript.setResultType(Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        redisTemplate.execute(redisScript, Collections.singletonList(lockKey), uuid);
        return null;
    }

    @GetMapping("/release")
    public Object release() {
        String lockKey = "123";
        String uuid = UUID.randomUUID().toString();
        boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,3, TimeUnit.MINUTES);
        if (!success){
           log.info("锁已存在");
        }
        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        redisTemplate.execute(redisScript, Collections.singletonList(lockKey),uuid);
        return null;
    }

    @GetMapping("/inc")
    public void inc() {
        String KEY = "HSJ-QPS-INCR";
        RedisAtomicLong redisAtomicLong = new RedisAtomicLong(KEY, redisTemplate.getConnectionFactory());
        redisAtomicLong.incrementAndGet();
    }

    // http://localhost:8080/lua/update?maxTime=600&minTime=10
    @GetMapping("/update")
    public Object update(@RequestParam("minTime") String minTime, @RequestParam("maxTime") String maxTime) {
        String maxKey = "maxTime";
        String minKey = "minTime";
        int min = Integer.parseInt(minTime);
        int max = Integer.parseInt(maxTime);
        redisTemplate.opsForValue().setIfAbsent(max, min, 3, TimeUnit.MINUTES);
        // 执行 lua 脚本
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        // 先获取指定key的值，然后和传入的arg比较是否相等，相等值删除key，否则直接返回0。
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("file/update.lua")));
        // 指定返回类型
        redisScript.setResultType(Long.class);
        List<String> keys = new ArrayList<>();
        keys.add(maxKey);
        keys.add(minKey);
        // 先存入90的时间
        redisTemplate.opsForValue().set(maxKey, 90);
        redisTemplate.opsForValue().set(minKey, 60);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        Object execute = redisTemplate.execute(redisScript, keys, max, min);
        return "执行完成！";
    }

    // http://localhost:8080/lua/updateTime?time=690
    @GetMapping("/updateTime")
    public void updateTime(@RequestParam("time") String time) {
        int paramTime = Integer.parseInt(time);
        // 执行 lua 脚本
        DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        // 先获取指定key的值，然后和传入的arg比较是否相等，相等值删除key，否则直接返回0。
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("file/update.lua")));
        // 指定返回类型
        redisScript.setResultType(Object.class);
        List<String> keyList = new ArrayList<>();
        // 会报此错误：CROSSSLOT Keys in request don't hash to the same slot--keys不能落在同一个节点
        //上面的代码传入了两个key，key1、key2，但算slot的时候发现并未落在同一个slot里面。
        //针对提示，那就让keys落在同一个slot里面就行啦
        //使用hash tag(hash tag是用于hash的部分字符串开始和结束的标记)，即如果key的结构为{XXX}key，则只会对{}里面的XXX进行分区，就会落到同一个slot里面啦。
        keyList.add("{test}" + "maxTime");
        keyList.add("{test}" + "minTime");
        keyList.add("{test}" + "myTest");
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        Object execute = redisTemplate.execute(redisScript, keyList, paramTime);
        log.info("结果,{}", execute);
    }

}
